﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BinaryTree;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows;
using Seta;
using CommomFunctions;

namespace Árvore_Binária_de_Pesquisa
{
    public class TreeStructure
    {
        /// <summary>
        /// bt -> the binary tree where all the operations will be executed on
        /// numOfLevels -> the maximum number of levels in the tree
        /// wasRemoval -> indicates if it is a removal operation or not
        /// foundNodeToRemoveFromTree -> indicates if the node to remove from the tree was found or not
        /// sentence -> the sentence that will be written to the trace
        /// color -> the color that will be used to write a certain sentence to the trace
        /// </summary>
        private TBinarySTree bt = new TBinarySTree();
        private int numOfLevels = 4;
        private bool wasRemoval = false;
        private bool foundNodeToRemoveFromTree = false;
        private string sentence;
        private Brush color;

        public bool foundNodeToRemove
        {
            get { return foundNodeToRemoveFromTree; }
        }

        public bool isRemoveOperation
        {
            get { return wasRemoval; }
            set { wasRemoval = value; }
        }

        /// <summary>
        /// Tests if the tree is empty or not
        /// </summary>
        /// <returns></returns>
        public bool isTreeEmpty()
        {
            if ((bt.count() < 1)||(bt.getRoot()== null))
                return true;

            return false;
        }

        /// <summary>
        /// Calls the function that creates a completly 4 levels tree and set all Labels and Arrows to visible
        /// </summary>
        /// <param name="myLabels"></param>
        /// <param name="myArrows"></param>
        public void showCompleteTree(Labels myLabels, Arrows myArrows)
        {
            bt.clear();
            createCompleteTree(myLabels);

            myArrows.setAllVisible();
            myLabels.setAllVisible();
        }

        /// <summary>
        /// Creates a completly 4 levels tree
        /// </summary>
        /// <param name="myLabels"></param>
        private void createCompleteTree(Labels myLabels)
        {
            myLabels.insertSortedLabels(bt);
        }

        /// <summary>
        /// Creates an empty tree
        /// </summary>
        /// <param name="myLabels"></param>
        /// <param name="myArrows"></param>
        public void createEmptyTree(Labels myLabels, Arrows myArrows)
        {
            bt.clear();

            myArrows.setAllHidden();
            myLabels.setAllHidden();
        }

        /// <summary>
        /// Destroies the existent tree
        /// </summary>
        /// <param name="myLabels"></param>
        /// <param name="myArrows"></param>
        public void destroyTree(Labels myLabels, Arrows myArrows)
        {
            createEmptyTree(myLabels, myArrows);
        }
        
        /// <summary>
        /// Calls the function that tries to insert a node in the tree.
        /// If the node was inserted, it calls the function to draw the new tree
        /// </summary>
        /// <param name="txtInput"></param>
        /// <param name="txtTrace"></param>
        /// <param name="myLabels"></param>
        /// <param name="myArrows"></param>
        public void insertTreeNode(TextBox txtInput, Trace txtTrace, Labels myLabels, Arrows myArrows)
        {
            if (bt.insertToHeight(txtInput.Text, numOfLevels) == null)
                MessageBox.Show("Devido às restrições de espaço na interface, a altura máxima da árvore é " + 4 + ".\nO nodo " + txtInput.Text + " não pode ser inserido");
            else
            {
                drawTree(bt.getRoot(), myLabels, myArrows);
            
                if (!bt.nodoJaInserido)
                    txtTrace.writeTrace("Insere nodo " + txtInput.Text, Brushes.SeaGreen);
            }

            txtInput.Text = "";
        }

        /// <summary>
        /// Calls the function that starts the "search for" or "remove" a node animation
        /// </summary>
        /// <param name="txtInput"></param>
        /// <param name="txtTrace"></param>
        /// <param name="cv"></param>        
        private void searchAndRemoveTreeNode(TextBox txtInput, Trace txtTrace, ControlVariables cv)
        {
            txtTrace.writeTrace("Procurando nodo " + txtInput.Text, Brushes.SpringGreen);

            TTreeNode ttn = bt.findSymbol(txtInput.Text);
            Array positions = new int[bt.wereSearched.Count];

            foreach (int i in bt.wereSearched)
            {
                positions.SetValue(i, bt.wereSearched.IndexOf(i));
            }

            if (ttn != null)
            {
                if (isRemoveOperation)
                {
                    foundNodeToRemoveFromTree = true;
                    sentence = "Removendo nodo " + txtInput.Text;
                }
                else sentence = "Nodo encontrado.";
                color = Brushes.SpringGreen;
            }
            else
            {
                if (isRemoveOperation)
                    foundNodeToRemoveFromTree = false;

                sentence = "Nodo não encontrado";
                color = Brushes.Red;
            }

            cv.updateSenteceAndColor(sentence, color);
            cv.animateSearchAndRemove(isRemoveOperation, positions);
            
        }

        /// <summary>
        /// Calls the function that starts the "remove a node" operation
        /// </summary>
        /// <param name="txtInput"></param>
        /// <param name="txtTrace"></param>
        /// <param name="cv"></param>        
        public void startRemoveTreeNodeAnimation(TextBox txtInput, Trace txtTrace, ControlVariables cv)
        {
            isRemoveOperation = true;
            searchAndRemoveTreeNode(txtInput, txtTrace, cv);
        }

        /// <summary>
        /// Calls the function that starts the "search for a node" operation
        /// </summary>
        /// <param name="txtInput"></param>
        /// <param name="txtTrace"></param>
        /// <param name="cv"></param>
        public void startSearchTreeNodeAnimation(TextBox txtInput, Trace txtTrace, ControlVariables cv)
        {
            isRemoveOperation = false;
            searchAndRemoveTreeNode(txtInput, txtTrace, cv);
        }
        
        /// <summary>
        /// Recursively draws the nodes and arrows by setting their visibility
        /// </summary>
        /// <param name="node"></param>
        /// <param name="ind"></param>
        /// <param name="myLabels"></param>
        /// <param name="myArrows"></param>
        /// <returns></returns>
        private string drawNode(TTreeNode node, int ind, Labels myLabels, Arrows myArrows)
        {
            if (node == null)
                return "empty";

            myLabels.setVisibility(ind, Visibility.Visible);
            myLabels.setContent(ind, node.name);

            if (ind > 0)
                myArrows.setVisibility(ind - 1, Visibility.Visible);

            if ((node.left == null) && (node.right == null))
                return node.name;

            if ((node.left != null) && (node.right == null))
                return node.name + "(" + drawNode(node.left, ((BinaryTree.TBinarySTree.inds)bt.aList[ind]).indLeft, myLabels, myArrows) + ", _)";

            if ((node.right != null) && (node.left == null))
                return node.name + "(_, " + drawNode(node.right, ((BinaryTree.TBinarySTree.inds)bt.aList[ind]).indRight, myLabels, myArrows) + ")";

            return node.name + "(" + drawNode(node.left, ((BinaryTree.TBinarySTree.inds)bt.aList[ind]).indLeft, myLabels, myArrows) + ", " + drawNode(node.right, ((BinaryTree.TBinarySTree.inds)bt.aList[ind]).indRight, myLabels, myArrows) + ")";
        }

        /// <summary>
        /// Calls the functions that create an index list of each node's children and draw the nodes
        /// </summary>
        /// <param name="root"></param>
        /// <param name="myLabels"></param>
        /// <param name="myArrows"></param>
        /// <returns></returns>
        private string drawTree(TTreeNode root, Labels myLabels, Arrows myArrows)
        {
            bt.createIndexList();

            return drawNode(root, 0, myLabels, myArrows);
        }

        public TBinarySTree bTree
        {
            get { return this.bt; }
        }

        /// <summary>
        /// Calls the function that tries to find the node indicated by "node".
        /// If it finds the node, it returns the node's position.
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        internal int getNodePosition(string node)
        {
            if (bt.findSymbol(node) != null)
            {
                int posicao = bt.wereSearched.Count;
                return (int)bt.wereSearched[posicao - 1];
            }
            return -1;
        }

        /// <summary>
        /// Calls the function that tries to remove the node indicated by the user
        /// </summary>
        /// <param name="node">The node indicated by the user</param>
        /// <param name="removePredecessor">Indicates if the root will be replaced by its predecessor or not</param>
        internal void removeTreeNode(string node, bool removePredecessor)
        {
            if (removePredecessor)
            {
                bt.deletePred(node);
            }
            else bt.delete(node);

        }

        /// <summary>
        /// Calls the functions that draws the tree
        /// </summary>
        /// <param name="myLabels"></param>
        /// <param name="myArrows"></param>
        internal void drawTree(Labels myLabels, Arrows myArrows)
        {
            this.drawTree(this.bt.getRoot(), myLabels, myArrows);
            
        }
    }
}
